﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using XUCore.Extensions;
using XUCore.Files;
using XUCore.Helpers;
using XUCore.IO;
using FileInfo = XUCore.Files.FileInfo;
using Microsoft.AspNetCore.StaticFiles;

namespace XUCore.NetCore.Uploads
{
    /// <summary>
    /// 默认文件上传服务
    /// </summary>
    internal class DefaultFileUploadService : IFileUploadService
    {
        /// <summary>
        /// 上传分片文件
        /// </summary>
        /// <param name="request">参数</param>
        /// <param name="guid">临时目录名</param>
        /// <param name="chunk">分片文件名</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task UploadThunckAsync(FileUploadRequest request, string guid, string chunk, CancellationToken cancellationToken)
        {
            var dirPath = Path.Combine(request.FullPath, guid);//临时存储分片目录

            if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);

            var ext = request.FormFile.FileName[request.FormFile.FileName.IndexOf('.')..]; //文件扩展名

            var filePath = Path.Combine(dirPath, $"{chunk}{ext}");

            using (var stream = new FileStream(filePath, FileMode.Create))
            {
                await request.FormFile.CopyToAsync(stream, cancellationToken);
            }
        }
        /// <summary>
        /// 合并分片
        /// </summary>
        /// <param name="request">参数</param>
        /// <param name="guid">临时目录名</param>
        /// <param name="fileName">文件名</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<XUCore.Files.FileInfo> UploadThunckMergeAsync(FileUploadRequest request, string guid, string fileName, CancellationToken cancellationToken)
        {
            var temporary = Path.Combine(request.FullPath, guid);//临时文件夹
            string fileExt = Path.GetExtension(fileName);//获取文件后缀
            var files = Directory.GetFiles(temporary);//获得下面的所有文件
            var saveName = $"{Id.Guid}{fileExt}";
            var date = DateTime.Now;
            var path = System.IO.Path.Combine(request.RelativePath, date.ToString("yyyyMMdd"));
            var finalFilePath = Path.Combine(request.RootPath, path, saveName);//最终的文件名

            var info = new System.IO.FileInfo(finalFilePath);
            if (!info.Directory.Exists) info.Directory.Create();

            using (var fs = info.Create())
            {
                foreach (var part in files.OrderBy(x => x.Length).ThenBy(x => x))
                {
                    var bytes = System.IO.File.ReadAllBytes(part);
                    fs.Write(bytes, 0, bytes.Length);
                    File.Delete(part);//删除分块
                    bytes = null;
                }
                fs.Close();
                fs.Dispose();
            }

            Directory.Delete(temporary);//删除文件夹

            await Task.CompletedTask;

            new FileExtensionContentTypeProvider().TryGetContentType(finalFilePath, out string contentType);

            return new XUCore.Files.FileInfo(path, info.Length, fileName, guid)
            {
                SaveName = saveName,
                ContentType = contentType
            };
        }
        /// <summary>
        /// 上传文件。单文件
        /// </summary>
        /// <param name="request">参数</param>
        /// <param name="cancellationToken">取消令牌</param>
        public async Task<FileInfo> UploadAsync(FileUploadRequest request, CancellationToken cancellationToken = default)
        {
            if (request.FormFile == null || request.FormFile.Length < 1)
            {
                if (request.Request.Form.Files != null && request.Request.Form.Files.Any())
                    request.FormFile = request.Request.Form.Files[0];
            }

            if (request.FormFile == null || request.FormFile.Length < 1)
                throw new ArgumentNullException("请选择文件!");

            var res = await UploadHelper.SaveAsync(request.FormFile, request.RelativePath, request.RootPath, cancellationToken);

            res.Domain = request.Domain;

            new FileExtensionContentTypeProvider().TryGetContentType(res.FullPath, out string contentType);

            res.ContentType = contentType;

            return res;
        }
        /// <summary>
        /// 上传文件。多文件
        /// </summary>
        /// <param name="request">参数</param>
        /// <param name="cancellationToken">取消令牌</param>
        public async Task<IEnumerable<FileInfo>> UploadAsync(MultipleFileUploadRequest request, CancellationToken cancellationToken = default)
        {
            if (request.FormFiles == null || !request.FormFiles.Any())
            {
                if (request.Request.Form.Files != null && request.Request.Form.Files.Any())
                    request.FormFiles = request.Request.Form.Files.ToList();
            }

            if (request.FormFiles == null || !request.FormFiles.Any())
                throw new ArgumentNullException("请选择文件!");

            var tasks = new List<Task<FileInfo>>();
            foreach (var formFile in request.FormFiles)
                tasks.Add(UploadHelper.SaveAsync(formFile, request.RelativePath, request.RootPath, cancellationToken));

            var files = await Task.WhenAll(tasks);

            foreach (var file in files)
            {
                new FileExtensionContentTypeProvider().TryGetContentType(file.FullPath, out string contentType);

                file.ContentType = contentType;
                file.Domain = request.Domain;
            }

            return files;
        }

        /// <summary>
        /// 上传图片。单张图片
        /// </summary>
        /// <param name="request">参数</param>
        /// <param name="cancellationToken">取消令牌</param>
        public async Task<ImageFileInfo> UploadImageAsync(ImageUploadRequest request, CancellationToken cancellationToken = default)
        {
            if (request.FormFile == null || request.FormFile.Length < 1)
            {
                if (request.Request.Form.Files != null && request.Request.Form.Files.Any())
                    request.FormFile = request.Request.Form.Files[0];
            }

            if (request.FormFile == null || request.FormFile.Length < 1)
                throw new ArgumentNullException("请选择文件!");

            string fileExt = FileHelper.GetExtension(request.FormFile.FileName);

            if (String.IsNullOrEmpty(fileExt) || Array.IndexOf(request.Extensions, fileExt.ToLower()) == -1)
                throw new Exception("上传图片扩展名是不允许的扩展名。只允许" + request.Extensions.Join(",") + "格式。");

            if (request.FormFile.Length > request.Size)
                throw new Exception($"上传文件大小超过限制。图片大小不能超过{new FileSize(request.Size, FileSizeUnit.Byte)}。");

            var imageInfo = await UploadHelper.SaveImageAsync(request.FormFile, request.RelativePath, request.RootPath, cancellationToken);

            new FileExtensionContentTypeProvider().TryGetContentType(imageInfo.FullPath, out string contentType);

            imageInfo.ContentType = contentType;

            return await UploadHelper.ImageProcessingAsync(request, imageInfo, cancellationToken);
        }

        /// <summary>
        /// 上传Base64图片。单张图片
        /// </summary>
        /// <param name="request">参数</param>
        /// <param name="cancellationToken">取消令牌</param>
        public async Task<ImageFileInfo> UploadImageAsync(Base64ImageUploadRequest request, CancellationToken cancellationToken = default)
        {
            if (request.Base64String.IsEmpty())
                throw new ArgumentNullException("请传递图片Base64字符串!");

            var imageInfo = await UploadHelper.SaveImageAsync(request.Base64String, request.RelativePath, request.RootPath, cancellationToken);

            new FileExtensionContentTypeProvider().TryGetContentType(imageInfo.FullPath, out string contentType);

            imageInfo.ContentType = contentType;

            return await UploadHelper.ImageProcessingAsync(request, imageInfo, cancellationToken);
        }
    }
}